package lists;

import runners.Runner;
import tester.cobertura.Instrument;
import java.util.*;

/**
 * This has been converted from an interface to an abstract class 
 * to take advantage of common code to implement methods like contains.
 * A List forces its subclasses, (ConsList & MTList), to implement it's methods.
 * @author Adam
 */
@Instrument
public abstract class List<T> extends ArrayList<T>{
	/**
   * 
   */
  private static final long serialVersionUID = 1L;

  /**
	 * OrMap applies the given predicate to corresponding elements of 
	 * the lists in sequence until either the list runs out, or predicate returns a true value.
	 * @param f
	 * @return boolean
	 */
	public abstract boolean ormap(UnaryFunction<Boolean, T> f);
	
	/**
	 * Map applies the given function element-wise to the elements of 
	 * the list and returns a list of the results, in order.
	 * @param <R> Return type of the Function function object.
	 * @param f
	 * @return List<R>
	 */
	public abstract <R> List<R> map(UnaryFunction<R, T> f);
	
	/**
	 * Filter returns all the elements of list that satisfy the given predicate. 
	 * The list is not disordered -- elements that appear in the result list occur 
	 * in the same order as they occur in the argument list.
	 * @param f
	 * @return List<T>
	 */
	public abstract List<T> filter(UnaryFunction<Boolean, T> f);
	 
	/**
	 * Count returns the integer value representing how many elements are in the list 
	 * that satisfy the given predicate.
	 * @param f
	 * @return int
	 */
	public abstract int count(UnaryFunction<Boolean, T> f);
	
	/**
	 * ToString returns the string representation of a list, in this case it puts spaces in between.
	 * @see java.lang.Object#toString()
	 * @return String
	 */
	public abstract String toString();
	
	/**
	 * Insert inserts an object of type T 
	 */
	public abstract List<T> insert(BinaryFunction<Boolean, T, T> f, T t);
	
	/**
	 * Sort returns the List<T> sorted based on the given function
	 */
	public abstract List<T> sort(BinaryFunction<Boolean, T, T> f);
	
	public abstract <R> R foldr(BinaryFunction<R, T, R> f, R base);
	
	/**
	 * Contains returns the boolean value stating whether the given object is a member of the list.
	 * @param o
	 * @return boolean
	 */
	public boolean contains(final Object o) {
		return this.ormap(new UnaryFunction<Boolean, T>() {
			public Boolean f(T t){
				// Checks if the list member t is equal to the Object o.
				return t.equals(o);
			}
		});
	}
}
